home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / AlertLib / AlertLib.c next >
Encoding:
C/C++ Source or Header  |  1994-01-10  |  10.9 KB  |  366 lines  |  [TEXT/KAHL]

  1. /* Revision History:
  2.     
  3.     94/01/09 aih
  4.     - changed how background notification options are set so they
  5.     can be loaded from a resource
  6.     
  7.     93/03/25 AIH
  8.     - Checks for Notification Manager before posting a notification
  9.     
  10.     91/11/14 AIH
  11.     - Uses the operating system utilities to capitalize the alert's control title
  12.     to check for equivalence with a command-key (instead of using toupper()).
  13.     
  14.     91/07/03 AIH
  15.     - Added a postcondition to one of the functions
  16.     
  17.     91/06/13 AIH
  18.     - Removed definitions of low-memory globals since they're already
  19.     defined in a more central file
  20.     - Fixed bug which caused the sound routine to be ignored if the
  21.     dialog was invisible
  22.     - Clarified a few comments
  23.     
  24.     91/05/10 AIH
  25.     - Uses an 'ALRT' resource and handles the stage list in much the same way
  26.     as the standard alert function, as described in IM-1
  27.     - Adapted to work with Apple Event manager -- calls AEInteractWithUser
  28.     (via the event library) if an Apple Event is being executed
  29.     
  30.     91/05/08 AIH
  31.     - Adapted to return error codes if the alert couldn't be created
  32.     
  33.     91/04/24 AIH
  34.     - Added user supplied filter
  35.     
  36.     91/04/22 AIH
  37.     - Modified the names of a few functions and constants
  38.     
  39.     91/04/17 AIH
  40.     - The filter function saves and restores the current port
  41.     
  42.     91/04/03 AIH
  43.     - Icon is drawn on update events, instead of using a user item
  44.     
  45.     91/03/25 AIH
  46.     - Adapted for new way of using event library
  47.     
  48.     91/03/23 AIH
  49.     - The "posted notification" flag wasn't getting reset when the notification
  50.     was removed
  51.     
  52.     91/03/19 AIH
  53.     - Added function for setting background notification options
  54.     
  55.     91/03/10-12 AIH
  56.     - Uses the window library to determine the kind of window
  57.     - String for alert is passed as a parameter, and set in first static
  58.     text item
  59.     - Icon is set in first user item
  60.     - Command key equivalents of buttons are supported
  61.     
  62.     91/03/07 AIH
  63.     - A filter procedure redraws the alert on update events
  64.     - Alerts are now really dialogs
  65.     
  66.     91/01/21 AIH
  67.     - Got rid of method of setting the titles of buttons in an alert.
  68.     This didn't work well, caused annoying flicker, and was complicated.
  69.     Also simplified the library.
  70.     - Added use of DlgErrSetup
  71.     
  72.     91/01/05 Ari Halberstadt
  73.     - Inserted this standard header in all files */
  74.  
  75. #include <stdarg.h>
  76. #include <string.h>
  77. #include "AlertLib.h"
  78. #include "EventLib.h"
  79. #include "GlobalLib.h"
  80. #include "LowMemLib.h"
  81. #include "MacLib.h"
  82. #include "MemoryLib.h"
  83. #include "MenuLib.h"
  84. #include "ResourceLib.h"
  85. #include "StringLib.h"
  86. #include "WindowLib.h"
  87.  
  88. static AlertOptionsType gAlertOptions;
  89.  
  90. /* set background notification options */
  91. void AlertOptionsSet(const AlertOptionsType *options)
  92. {
  93.     gAlertOptions = *options;
  94. }
  95.  
  96. /* If we're in the background, then post a notification and enter a
  97.     nested event loop. When the application is brought to the foreground
  98.     the nested event loop is exited. Since the event loop is recursive,
  99.     and since the only static variable is a flag indicating that we've already
  100.     posted a notification, it is possible for the application to queue any
  101.     number of alerts while it is in the background. The alerts will be displayed
  102.     in the reverse of the order they were posted (i.e., last alert to be posted
  103.     appears first) and only one alert will actually be visible on the screen at
  104.     a time. All of this is done according to the HIG, which specify that alerts
  105.     shouldn't appear while an application is in the background and that
  106.     multiple background notifications should be avoided. */
  107. static void NotifyIfInBackground(void)
  108. {
  109.     static Boolean         posted;            /* true if a notification has been posted */
  110.     static NMRec            notification;    /* the notification */
  111.     CStr255                    string;            /* string to display in notification */
  112.     volatile Handle        sicn = NULL;    /* small icon to display in menu bar */
  113.     volatile Handle        sound = NULL;    /* sound to play */
  114.     volatile SignedByte    soundState = 0;/* saved state of handle to sound resource */
  115.     volatile SignedByte    sicnState = 0;    /* saved state of handle to small icon resource */
  116.  
  117.     TRY {
  118.         if (MacHasNotificationMgr() &&
  119.              EventInBackground() &&
  120.              gAlertOptions.flags != ALERT_NOTIFY_NONE)
  121.         {
  122.             if (! posted) {
  123.                 /* configure notification according to options */
  124.                 memclr(¬ification, sizeof(NMRec));
  125.                 notification.qType = nmType;
  126.                 
  127.                 /* set mark character */
  128.                 if ((gAlertOptions.flags & ALERT_NOTIFY_MARK) != 0)
  129.                     notification.nmMark = gAlertOptions.mark;
  130.         
  131.                 /* set small icon */
  132.                 sicn = NULL;
  133.                 if ((gAlertOptions.flags & ALERT_NOTIFY_ICON) != 0) {
  134.                     sicn = ResGet('SICN', gAlertOptions.sicn);
  135.                     sicnState = HandleNoPurge(sicn);
  136.                     notification.nmIcon = sicn;
  137.                 }
  138.                 
  139.                 /* set alert string */
  140.                 if ((gAlertOptions.flags & ALERT_NOTIFY_ALERT) != 0) {
  141.                     ResString(gAlertOptions.string, string);
  142.                     if (*string)
  143.                         notification.nmStr = (StringPtr) c2pstr(string);
  144.                 }
  145.                 
  146.                 /* set sound */
  147.                 sound = NULL;
  148.                 if ((gAlertOptions.flags & ALERT_NOTIFY_SOUND) != 0) {
  149.                     if (gAlertOptions.sound == -1)
  150.                         notification.nmSound = (Handle) -1L;
  151.                     else {
  152.                         sound = ResGet('snd ', gAlertOptions.sound);
  153.                         soundState = HandleNoPurge(sound);
  154.                         notification.nmSound = sound;
  155.                     }
  156.                 }
  157.                 
  158.                 /* post notification */
  159.                 FailOSErr(NMInstall(¬ification));
  160.                 posted = true;
  161.             }
  162.             
  163.             /* handle events till the application is brought to the foreground */
  164.             while (EventInBackground())
  165.                 EventOne();
  166.         }
  167.     } CLEANUP {
  168.         if (sicn) HandleRestore(sicn, sicnState);
  169.         if (sound) HandleRestore(sound, soundState);
  170.         if (posted) {
  171.             NMRemove(¬ification);
  172.             posted = false;
  173.         }
  174.     } ENDTRY;
  175. }
  176.  
  177. /* Standard filter for alerts; handles command key equivalents for
  178.     controls and draws the alert's icon. */
  179. Boolean AlertFilter(DialogPtr dlg, EventRecord *event, short *item,
  180.     void *data)
  181. {
  182.     short     nitems;    /* number of items in dialog */
  183.     short     click;    /* item to click */
  184.     char        key;        /* key pressed */
  185.     CStr255    title;    /* title of control */
  186.     Handle    icon;        /* icon to draw */
  187.     Rect        box;        /* icon's enclosing rectangle */
  188.     GrafPtr    port;        /* saved port */
  189.     Boolean result = false;
  190.     
  191.     /* setup port */
  192.     GetPort(&port);
  193.     SetPort(dlg);
  194.     
  195.     /* interpret command key combinations as clicks in controls */
  196.     switch (event->what) {
  197.     case keyDown:
  198.     case autoKey:
  199.         if (event->modifiers & cmdKey) {
  200.     
  201.             /* search for control whose first character matches the key pressed */
  202.             key = event->message;
  203.             nitems = DlgNItems(dlg);
  204.             for (click = 1; ! result && click <= nitems; click++) {
  205.             
  206.                 /* make sure item is an enabled control */
  207.                 if (DlgTypeCtl(DlgType(dlg, click)) && DlgCtlEnabled(dlg, click)) {
  208.                     Str31 keystr;
  209.                     
  210.                     /* compare first character of control's title to key */
  211.                     CtlTitle(DlgCtl(dlg, click), title);
  212.                     keystr[0] = 1; keystr[1] = key;
  213.                     UprString(keystr, true);
  214.                     UprString(c2pstr(title), true);
  215.                     if (keystr[1] == title[1]) {
  216.                     
  217.                         /* simulate a click on the control */
  218.                         *item = click;
  219.                         result = true;
  220.                         event->what = nullEvent;
  221.                         DlgFlashButton(dlg, click);
  222.                     }
  223.                 }
  224.             }
  225.         }
  226.         break;
  227.     case updateEvt:
  228.         /* draw the icon */
  229.         if (WinExtraPtr(dlg)->icon != ALERT_NO_ICON) {
  230.             SetRect(&box, 20, 10, 52, 42);
  231.             icon = GetIcon(WinExtraPtr(dlg)->icon);
  232.             if (icon)
  233.                 PlotIcon(&box, icon);
  234.         }
  235.         break;
  236.     }
  237.     SetPort(port);
  238.     return(result);
  239. }
  240.  
  241. /* Display an alert using the 'ALRT' template with ID 'id', and display
  242.     the icon with ID 'iconid'. The 'filter' parameter can be NULL, in
  243.     which case the standard filter AlertFilter will be called, otherwise it
  244.     should point to a modal dialog filter. The 'data' parameter will be passed
  245.     to the filter function. The 'nstrings' parameter indicates the
  246.     number of strings in the alert. Subsequent parameters will set static text
  247.     items to the strings: the first static text item will be set to the first
  248.     string, the second static text item will be set to the second string, etc.
  249.     The item used to close the alert is returned. */
  250. short AlertCustom(short id, short iconid, DlgModalFilterType filter,
  251.     void *data, short nstrings, ...)
  252. {
  253.     volatile DialogPtr dlg = NULL;    /* the dialog */
  254.     AlertTHndl    alrt = NULL;/* alert's template */
  255.     va_list        ap;            /* for getting string parameters */
  256.     short            i = 0;        /* index to dialog's items for setting the strings */
  257.     short            show = 0;    /* if true then alert is drawn */
  258.     short            sound = 0;    /* number of beeps */
  259.     short            dflt = 0;    /* default item */
  260.     short            shift = 0;    /* number of bits to shift */
  261.     short            item = 0;    /* item clicked in alert */
  262.     StageList    stages = 0;    /* alert's stage list */            
  263.     
  264.     TRY {
  265.     
  266.         /* reset alert stage if this isn't the same alert as the previous alert */
  267.         if (id != GetANumber())
  268.             ResetAlrtStage();
  269.             
  270.         /* set default values for stage list parameters */
  271.         show = true;
  272.         sound = 1;
  273.         dflt = ok;
  274.         
  275.         /* Examine the alert's StageList. This is a really ugly set of masks and
  276.             shifts which get the sound number, the default button number,
  277.             and whether to display the alert for the current stage. */
  278.         alrt = (AlertTHndl) ResGet('ALRT', id);
  279.     
  280.         /* each stage uses 4 bits */
  281.         stages = (**alrt).stages;
  282.         shift = 4 * GetAlrtStage();
  283.         
  284.         /* get the fields for the current stage by masking off all the
  285.             other bits */
  286.         sound = (stages & (0x03 << shift));
  287.         show = (stages & (0x04 << shift));
  288.         dflt = (stages & (0x08 << shift));
  289.         
  290.         /* shift the bit fields right so they start from 0 */
  291.         sound = (((unsigned short) sound) >> (shift + 0));
  292.         show = (((unsigned short) show) >> (shift + 2));
  293.         dflt = (((unsigned short) dflt) >> (shift + 3)) + 1;
  294.     
  295.         /* only create the dialog if something would happen */
  296.         if (show || sound) {
  297.         
  298.             /* start interacting with the user */
  299.             EventInteractWithUser();
  300.                 
  301.             /* use notification manager if the application is in the background */
  302.             NotifyIfInBackground();
  303.                 
  304.             /* set up the dialog and then run it */
  305.             if (show) {
  306.             
  307.                 /* create dialog */
  308.                 dlg = DlgBeginAlert(id);
  309.                 
  310.                 /* set static text items */
  311.                 ap = va_start(ap, nstrings);
  312.                 for (i = 1; i <= DlgNItems(dlg) && nstrings > 0; i++) {
  313.                     if (DlgTypeStatText(DlgType(dlg, i))) {
  314.                         DlgTextSet(dlg, i, va_arg(ap, char *));
  315.                         nstrings--;
  316.                     }
  317.                 }
  318.                 va_end(ap);
  319.                 
  320.                 /* setup dialog */
  321.                 if (! filter)
  322.                     filter = AlertFilter;
  323.                 WinExtraPtr(dlg)->icon = iconid;        
  324.                 DlgModalFilterSet(dlg, filter, data);
  325.                 DlgDefaultSet(dlg, dflt);
  326.                 DlgPosition(dlg);
  327.                 WinShow(dlg);
  328.                 
  329.                 /* call sound routine immediately before running alert */
  330.                 if (sound && GetDABeeper())
  331.                     GetDABeeper()(sound);
  332.                         
  333.                 /* run dialog until an enabled item is hit */
  334.                 item = DlgModalRun(dlg);
  335.             }
  336.             else {
  337.                 /* not showing alert, so just call sound routine */
  338.                 if (sound && GetDABeeper())
  339.                     GetDABeeper()(sound);
  340.             }
  341.         }
  342.     } CLEANUP {
  343.         DlgEnd(dlg);
  344.     } ENDTRY;
  345.     ensure(show ? item > 0 : item == 0);
  346.     return(item);
  347. }
  348.  
  349. /* display a stop alert */
  350. short AlertStop(short id, CStr255 str)
  351. {
  352.     return(AlertCustom(id, stopIcon, NULL, NULL, 1, str));
  353. }
  354.  
  355. /* display a note alert */
  356. short AlertNote(short id, CStr255 str)
  357. {
  358.     return(AlertCustom(id, noteIcon, NULL, NULL, 1, str));
  359. }
  360.  
  361. /* display a caution alert */
  362. short AlertCaution(short id, CStr255 str)
  363. {
  364.     return(AlertCustom(id, cautionIcon, NULL, NULL, 1, str));
  365. }
  366.